package com.ksyun.campus.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.ksyun.campus.client.domain.*;
import com.ksyun.campus.client.util.HttpClientConfig;
import com.ksyun.campus.client.util.HttpClientUtil;
import com.ksyun.campus.client.util.ZkUtil;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

public class EFileSystem extends FileSystem {
    private String fileSystemName;
    private String metaServerBaseUri;
    private HttpClientConfig HttpClientConfig;

    @Resource
    private ZkUtil zkUtil;

    private HttpClient httpClient;

    public EFileSystem() throws Exception {
        this("default");
    }

    public EFileSystem(String fileSystemName) throws Exception {
        this.fileSystemName = fileSystemName;
        HttpClientConfig = new HttpClientConfig();
        zkUtil = new ZkUtil();
        metaServerBaseUri = zkUtil.getMetaServer();
        httpClient = HttpClientUtil.createHttpClient(HttpClientConfig);
    }

    public FSInputStream open(String path) throws IOException {


        return new FSInputStream(httpClient, path, fileSystemName);
    }


    //创建一个空文件
    public FSOutputStream create(String path) throws ParseException, UnsupportedEncodingException {
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
        // 向 metaServer 请求创建文件，请求头里面有 fileSystemName
        String url = metaServerBaseUri + "/create?path=" + encodedPath;
        HttpPost httpPost = new HttpPost(url);

        // 将 fileSystemName 添加到请求头
        httpPost.setHeader("fileSystemName", fileSystemName);

        // 将路径作为请求参数
        StringEntity entity = new StringEntity("path=" + path);
        httpPost.setEntity(entity);

        // 发送请求
        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost)) {

            String jsonResponse = EntityUtils.toString(response.getEntity());
            ObjectMapper objectMapper = new ObjectMapper();
            Result result = objectMapper.readValue(jsonResponse, Result.class);

            if (result.getCode().equals("200")) {
                // 处理成功响应
                return new FSOutputStream(httpClient, path, fileSystemName);
            } else {
                // 处理失败响应
                System.out.println("创建文件失败: " + response.getCode());
                throw new IOException("创建文件失败: " + response.getCode());

            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }

    //创建一个空文件夹
    public boolean mkdir(String path) throws UnsupportedEncodingException, ParseException {
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
        String url = metaServerBaseUri + "/mkdir?path=" + encodedPath;
        HttpPost httpPost = new HttpPost(url);

        httpPost.setHeader("fileSystemName", fileSystemName);

        StringEntity entity = new StringEntity("path=" + path);
        httpPost.setEntity(entity);
        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost)) {

            String jsonResponse = EntityUtils.toString(response.getEntity());
            ObjectMapper objectMapper = new ObjectMapper();
            Result result = objectMapper.readValue(jsonResponse, Result.class);

            if (result.getCode().equals("200")) {
                return true;
            } else {
                // 处理失败响应
                System.out.println("创建文件夹失败: " + response.getCode());
                throw new IOException("创建文件夹失败: " + response.getCode());

            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    //获取文件信息
    public StatInfo getFileStats(String path) throws UnsupportedEncodingException, ParseException {
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
        String url = metaServerBaseUri + "/stats?path=" + encodedPath;
        HttpPost httpPost = new HttpPost(url);

        httpPost.setHeader("fileSystemName", fileSystemName);

        StringEntity entity = new StringEntity("path=" + path);
        httpPost.setEntity(entity);

        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost)) {

            String jsonResponse = EntityUtils.toString(response.getEntity());
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
            Result result = objectMapper.readValue(jsonResponse, new TypeReference<Result<FileInfo>>() {
            });

            if (result.getCode().equals("200")) {
                FileInfo fileInfo = (FileInfo) result.getData();
                return fileinfoToStatInfo(fileInfo, path);
            } else {
                // 处理失败响应
                System.out.println("获取项目元信息失败: " + response.getCode());
                throw new IOException("获取项目元信息失败: " + response.getCode());

            }
        } catch (IOException e) {
            e.printStackTrace();
            return new StatInfo();
        }
    }

    //获取文件夹下的所有项目信息
    public List<StatInfo> listFileStats(String path) throws IOException, ParseException {
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
        String url = metaServerBaseUri + "/listdir?path=" + encodedPath;
        HttpPost httpPost = new HttpPost(url);

        httpPost.setHeader("fileSystemName", fileSystemName);

        StringEntity entity = new StringEntity("path=" + path);
        httpPost.setEntity(entity);

        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost)) {
            String jsonResponse = EntityUtils.toString(response.getEntity());
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
            Result result = objectMapper.readValue(jsonResponse, new TypeReference<Result<List<FileInfo>>>() {
            });

            if (result.getCode().equals("200")) {
                List<FileInfo> fileInfoList = (List<FileInfo>) result.getData();
                List<StatInfo> statInfoList = new ArrayList<>();
                for (FileInfo fileInfo : fileInfoList) {
                    statInfoList.add(fileinfoToStatInfo(fileInfo, path));
                }
                return statInfoList;
            } else {
                // 处理失败响应
                System.out.println("获取文件夹下的所有项目信息失败: " + response.getCode());
                throw new IOException("获取文件夹下的所有项目信息失败: " + response.getCode());

            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }


    //删除文件，如果是文件夹则删除文件夹下的所有文件
    public boolean delete(String path) throws IOException {
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
        String url = metaServerBaseUri + "/delete?path=" + encodedPath;

        HttpDelete httpDelete = new HttpDelete(url);

        // 将 fileSystemName 添加到请求头
        httpDelete.setHeader("fileSystemName", fileSystemName);

        try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpDelete)) {
            String jsonResponse = EntityUtils.toString(response.getEntity());
            ObjectMapper objectMapper = new ObjectMapper();
            Result result = objectMapper.readValue(jsonResponse, Result.class);

            if (result.getCode().equals("200")) {
                System.out.println("删除成功: " + path);
                return true;
            } else {
                System.out.println("删除失败: " + result.getMsg());
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }


    //获取集群信息（mataServer、dataServer信息）
    public ClusterInfo getClusterInfo() {
        ClusterInfo clusterInfo = new ClusterInfo();
        List<MetaServerInfo> metaServers = zkUtil.getMetaServers();
        MetaServerMsg masterMetaServer = new MetaServerMsg();
        MetaServerMsg slaveMetaServer = new MetaServerMsg();
        if (metaServers.size() >= 1) {
            masterMetaServer.setHost(metaServers.get(0).getIpAddress());
            masterMetaServer.setPort(metaServers.get(0).getPort());
        }
        if (metaServers.size() >= 2) {
            slaveMetaServer.setHost(metaServers.get(1).getIpAddress());
            slaveMetaServer.setPort(metaServers.get(1).getPort());
        }
        clusterInfo.setMasterMetaServer(masterMetaServer);
        clusterInfo.setSlaveMetaServer(slaveMetaServer);

        List<DataServerMsg> targetDataServers = new ArrayList<>();
        List<DataServerInfo> dataServers = zkUtil.getDataServers();

        for (DataServerInfo dataServerInfo : dataServers) {
            targetDataServers.add(DataServerInfoToDataServerMsg(dataServerInfo));
        }
        clusterInfo.setDataServer(targetDataServers);
        return clusterInfo;
    }

    private DataServerMsg DataServerInfoToDataServerMsg(DataServerInfo dataServerInfo) {
        DataServerMsg dataServerMsg = new DataServerMsg();
        dataServerMsg.setHost(dataServerInfo.getIpAddress());
        dataServerMsg.setPort(dataServerInfo.getPort());

        dataServerMsg.setCapacity(dataServerInfo.getCapacity());
        dataServerMsg.setUseCapacity(dataServerInfo.getUseCapacity());
        dataServerMsg.setFileTotal(dataServerInfo.getFileTotal());

        return dataServerMsg;
    }

    private StatInfo fileinfoToStatInfo(FileInfo fileInfo, String path) {
        StatInfo statInfo = new StatInfo();
        statInfo.setType(FileType.get(fileInfo.getFolderType()));
        statInfo.setPath(fileInfo.getFilePath());
        statInfo.setMtime(fileInfo.getUpdateTime());
        statInfo.setSize(fileInfo.getFileSize());

        List<ReplicaData> replicaDataList = new ArrayList<>();
        String copyLocationOne = fileInfo.getCopyLocationOne();
        String copyLocationTwo = fileInfo.getCopyLocationTwo();
        String copyLocationThree = fileInfo.getCopyLocationThree();

        replicaDataList.add(copyLocationToReplicaData(copyLocationOne, fileInfo.getFilePath()));
        replicaDataList.add(copyLocationToReplicaData(copyLocationTwo, fileInfo.getFilePath()));
        replicaDataList.add(copyLocationToReplicaData(copyLocationThree, fileInfo.getFilePath()));

        statInfo.setReplicaData(replicaDataList);
        return statInfo;
    }

    private ReplicaData copyLocationToReplicaData(String fileInfo, String path) {
        if (fileInfo == null || fileInfo.isEmpty())
            return null;

        ReplicaData replicaData = new ReplicaData();
        replicaData.setId(fileInfo);

        if (fileInfo.startsWith("dataServer")) {
            String remaining = fileInfo.substring("dataServer".length());

            String[] parts = remaining.split("-", 2);

            if (parts.length == 2) {
                String part1 = "dataServer";
                String part2 = parts[0]; // ip
                String part3 = parts[1]; // port

                replicaData.setDsNode(part2 + ":" + part3);
                replicaData.setPath(path);
                return replicaData;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
}
